﻿//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  NotificationRulesTest.cs
//    
// Abstract:
//
//  Implements Unit tests for NotificationRules
//  
//--

using System;
using Microsoft.OfficeCommunicationsServer.Applications.Common;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.EnhancedReminders.OutlookAddIn.UnitTests
{
    /// <summary>
    /// Summary description for NotificationRulesTest
    /// </summary>
    [TestClass]
    public sealed class NotificationRulesTest
    {
        private const string StatusRuleName = "Test Status Rule";
        private const string TimeRuleName = "Test Time Rule";
        private const string CalendarRuleName = "Test Calendar Rule";

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        /// <summary>
        /// Test for TimeZone setting
        /// </summary>
        [TestMethod]
        public void TimeZoneDisplayNameTest()
        {
            NotificationRules rules = new NotificationRules();
            rules.TimeZone = "Pacific Standard Time";

            Assert.AreEqual<string>("Pacific Standard Time", rules.TimeZone, "Unexpected time zone display name");
        }

        /// <summary>
        /// Tests that NotificationRules.GetFirstMatchingRule works as expected.
        /// </summary>
        [TestMethod]
        public void TestGetFirstMatchingRule()
        {
            NotificationRules rules = CreateDefaultRules();

            NotificationData notificationData = new NotificationData(
                null, MailboxType.Voicemail, new DateTime(2008, 1, 15, 12, 0, 0, DateTimeKind.Utc) /* Weekday outside office hours */);

            NotificationRuleItem[] ruleItems = rules.GetMatchingRules(notificationData);

            Assert.IsNotNull(ruleItems, "No matching rules");
            Assert.AreEqual<int>(2, ruleItems.Length, "Unexpected no. of matching rules");
            Assert.AreEqual<string>(ruleItems[0].RuleName, StatusRuleName, "Unexpected matching first rule");
            Assert.AreEqual<string>(ruleItems[1].RuleName, TimeRuleName, "Unexpected matching second rule");

            notificationData = new NotificationData(
                null, MailboxType.Voicemail, new DateTime(2008, 1, 15, 20, 0, 0, DateTimeKind.Utc) /* Weekday during office hours */);

            ruleItems = rules.GetMatchingRules(notificationData);

            Assert.IsNotNull(ruleItems, "No matching rules");
            Assert.AreEqual<int>(1, ruleItems.Length, "Unexpected no. of matching rules");
            Assert.AreEqual<string>(ruleItems[0].RuleName, StatusRuleName, "Unexpected matching first rule");

            notificationData = new NotificationData(
                null, MailboxType.CalendarItem, new DateTime(2008, 1, 19, 20, 0, 0, DateTimeKind.Utc) /* Saturday during office hours */);

            ruleItems = rules.GetMatchingRules(notificationData);

            Assert.IsNotNull(ruleItems, "No matching rules");
            Assert.AreEqual<int>(1, ruleItems.Length, "Unexpected no. of matching rules");
            Assert.AreEqual<string>(ruleItems[0].RuleName, CalendarRuleName, "Unexpected matching first rule");
        }

        /// <summary>
        /// Tests that NotificationRules.UpdateRuleIds works as expected.
        /// </summary>
        [TestMethod]
        public void TestUpdateRuleIds()
        {
            // Create two identical sets of rules
            NotificationRules previousRules = CreateDefaultRules();
            NotificationRules newRules = CreateDefaultRules();

            Assert.IsNotNull(previousRules, "Rules hsould not be null");
            Assert.AreEqual<int>(3, previousRules.Count, "Unexpected no. of rules");
            Assert.IsNotNull(newRules, "Rules hsould not be null");
            Assert.AreEqual<int>(3, newRules.Count, "Unexpected no. of rules");

            // Create a new calendar rule
            NotificationRuleItem calendarRule = new NotificationRuleItem(MailboxType.CalendarItem, CalendarRuleName, DateTime.UtcNow, null);
            calendarRule.IsEnabled = true;
            calendarRule.Id = previousRules[2].Id;
            NotificationTimeCondition timeCondition = Helpers.AssertCast<NotificationTimeCondition>(calendarRule.GetCondition(typeof(NotificationTimeCondition)));
            timeCondition.IsSelected = true;
            timeCondition.TimeArray.SetAll(false);
            newRules.Add(calendarRule);
            Assert.AreEqual<int>(4, newRules.Count, "Unexpected no. of rules");

            // Verify rule IDs are as expected
            Assert.AreNotEqual<Guid>(previousRules[2].Id, newRules[2].Id, "Calendar rule IDs should not be the same");
            Assert.AreEqual<Guid>(previousRules[2].Id, newRules[3].Id, "Calendar rule IDs should be the same");

            // Update the rules
            newRules.UpdateRuleIds(previousRules);

            // The first calendar rule should have the same ID because the rules are identical
            Assert.AreEqual<Guid>(previousRules[2].Id, newRules[2].Id, "Calendar rule IDs should be the same");
            // The second calendar rule should have a new ID because it doesn't exist in the previous version of the rules
            Assert.AreNotEqual<Guid>(previousRules[2].Id, newRules[3].Id, "Calendar rule IDs should not be the same");

            // Add a copy of the new calendar rule to the previous version of the rules
            previousRules.Add(new NotificationRuleItem(calendarRule));
            Assert.AreEqual<int>(4, previousRules.Count, "Unexpected no. of rules");

            // Update the rule ID to something different
            previousRules[3].Id = Guid.NewGuid();
            Assert.AreNotEqual<Guid>(previousRules[3].Id, newRules[3].Id, "Calendar rule IDs should not be the same");

            // Update the rules
            newRules.UpdateRuleIds(previousRules);

            // The first calendar rule should have the same ID because the rules are identical
            Assert.AreEqual<Guid>(previousRules[2].Id, newRules[2].Id, "Calendar rule IDs should be the same");
            // The second calendar rule should have the same ID because the rules are identical
            Assert.AreEqual<Guid>(previousRules[3].Id, newRules[3].Id, "Calendar rule IDs should be the same");

            // Update the time condition in the first calendar rule
            timeCondition = Helpers.AssertCast<NotificationTimeCondition>(newRules[2].GetCondition(typeof(NotificationTimeCondition)));
            timeCondition.TimeArray.Set((NotificationTimeCondition.IntervalsPerDay * 5), true);

            // Update the rules
            newRules.UpdateRuleIds(previousRules);

            // The calendar rule IDs should be different because the first calendar rule has a different time condition
            Assert.AreNotEqual<Guid>(previousRules[2].Id, newRules[2].Id, "Calendar rule IDs should not be the same");
            Assert.AreNotEqual<Guid>(previousRules[3].Id, newRules[3].Id, "Calendar rule IDs should not be the same");
        }

        /// <summary>
        /// Test for IndexIntoBitArray algorithm
        /// </summary>
        [TestMethod]
        public void IndexIntoBitArrayTest()
        {
            // Midnight on Sunday
            DateTime dateTime = new DateTime(2007, 9, 23, 0, 0, 0);
            Assert.AreEqual<int>(0, NotificationTimeCondition_Accessor.IndexIntoArray(dateTime), "Sunday midnight is the first bit in the bit array");

            // Saturday night just before midnight
            dateTime = new DateTime(2007, 9, 22, 23, 59, 59);
            Assert.AreEqual<int>(NotificationTimeCondition.IntervalsPerDay * NotificationTimeCondition.NumberOfDays - 1, NotificationTimeCondition_Accessor.IndexIntoArray(dateTime),
                "Saturday night just before Sunday midnight is the last bit in the bit array");

            // Sunday night just before Monday midnight
            dateTime = new DateTime(2007, 9, 23, 23, 59, 59);
            Assert.AreEqual<int>(NotificationTimeCondition.IntervalsPerDay - 1, NotificationTimeCondition_Accessor.IndexIntoArray(dateTime),
                "Sunday night just before Monday midnight is the last slot for the day");
        }

        /// <summary>
        /// Helper method to create some default rules for the tests in this class.
        /// </summary>
        /// <returns>
        /// The rules
        /// </returns>
        private static NotificationRules CreateDefaultRules()
        {
            NotificationRules rules = new NotificationRules();
            rules.TimeZone = "Pacific Standard Time";

            // Set up Voicemails Away default rule
            NotificationRuleItem statusRule = new NotificationRuleItem(MailboxType.Voicemail, StatusRuleName, DateTime.UtcNow, null);
            statusRule.IsEnabled = true;

            NotificationStatusCondition statusCondition = Helpers.AssertCast<NotificationStatusCondition>(statusRule.GetCondition(typeof(NotificationStatusCondition)));
            statusCondition.IsSelected = true;
            statusCondition.Presence = PresenceStates.Away | PresenceStates.IdleBusy | PresenceStates.IdleOnline | PresenceStates.BeRightBack | PresenceStates.Offline;

            // Set up Off Hours default rule
            NotificationRuleItem timeRule = new NotificationRuleItem(MailboxType.Voicemail, TimeRuleName, DateTime.UtcNow, null);
            timeRule.IsEnabled = true;

            NotificationTimeCondition timeCondition = Helpers.AssertCast<NotificationTimeCondition>(timeRule.GetCondition(typeof(NotificationTimeCondition)));
            timeCondition.IsSelected = true;
            timeCondition.TimeArray.SetAll(true);

            // Unset working hours on weekdays
            // Index of 8am - 8.30am
            int workStartTimeIndex = (8 * 2);
            // Index 5.30pm - 6pm 
            int workEndTimeIndex = (18 * 2) - 1;

            int indexOfMonday = 1;
            int indexOfFriday = 5;

            for (int workDay = indexOfMonday; workDay <= indexOfFriday; workDay++)
            {
                for (int workHours = workStartTimeIndex; workHours <= workEndTimeIndex; workHours++)
                {
                    timeCondition.TimeArray.Set((NotificationTimeCondition.IntervalsPerDay * workDay) + workHours, false);
                }
            }

            // Set up calendar default rule
            NotificationRuleItem calendarRule = new NotificationRuleItem(MailboxType.CalendarItem, CalendarRuleName, DateTime.UtcNow, null);
            calendarRule.IsEnabled = true;

            timeCondition = Helpers.AssertCast<NotificationTimeCondition>(calendarRule.GetCondition(typeof(NotificationTimeCondition)));
            timeCondition.IsSelected = true;
            timeCondition.TimeArray.SetAll(false);

            for (int workHours = workStartTimeIndex; workHours <= workEndTimeIndex; workHours++)
            {
                timeCondition.TimeArray.Set((NotificationTimeCondition.IntervalsPerDay * 6) + workHours, true);
            }

            rules.Add(statusRule);
            rules.Add(timeRule);
            rules.Add(calendarRule);

            return rules;
        }
    }
}
